﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Browser;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using System.Threading.Tasks;
using C1.Net;
using System.Threading;

namespace ControlExplorer
{
    public class OneDriveHelper
    {
        #region ** uploading files to windows like - skydrive

        /// <summary>
        /// The URI for the OAuth service's Authorize endpoint.
        /// </summary>
        private static readonly string OAuthAuthorizeUri = "https://oauth.live.com/authorize";

        /// <summary>
        /// The URI for the API service endpoint.
        /// </summary>
        private static readonly string ApiServiceUri = "https://apis.live.net/v5.0/";

        /// <summary>
        /// The applications client ID.
        /// </summary>
        private static readonly string ClientId = "0000000040062F11";

        /// <summary>
        /// The applications redirect URI (does not need to exist).
        /// </summary>
        private static readonly string RedirectUri = "https://oauth.live.com/desktop";

        /// <summary>
        /// Holds the retrieved access token.
        /// </summary>
        private string accessToken;

        /// <summary>
        /// The list of scopes.
        /// </summary>
        private string[] scopes = new string[] { "wl.skydrive_update" };

        WebBrowser authorizationBrowser;
        TaskCompletionSource<bool> getAccessTokenCompleted;

        /// <summary>
        /// Launch the OAuth flow.
        /// </summary>
        public async Task GetAccessToken(WebBrowser authorizationBrowser)
        {
            if (string.IsNullOrWhiteSpace(accessToken))
            {
                this.authorizationBrowser = authorizationBrowser;
                this.getAccessTokenCompleted = new TaskCompletionSource<bool>();
                this.authorizationBrowser.Navigating += OnAuthorizationBrowserNavigating;
                this.authorizationBrowser.Navigated += OnAuthorizationBrowserNavigated;
                this.authorizationBrowser.NavigationFailed += OnAuthorizationBrowserNavigationFailed;
                this.authorizationBrowser.Navigate(BuildOAuthUri(this.scopes));
                await getAccessTokenCompleted.Task;
            }
        }

        /// <summary>
        /// Complete the OAuth flow.
        /// </summary>
        /// <param name="success">Whether the operation was successful.</param>
        private void CompleteOAuthFlow(bool success)
        {
            this.authorizationBrowser.Navigated -= this.OnAuthorizationBrowserNavigated;
            this.authorizationBrowser.Navigating -= this.OnAuthorizationBrowserNavigating;

            this.authorizationBrowser.NavigateToString(String.Empty);
            this.authorizationBrowser.Visibility = Visibility.Collapsed;

            if (success && getAccessTokenCompleted != null && !getAccessTokenCompleted.Task.IsCompleted)
            {
                getAccessTokenCompleted.SetResult(true);
            }
        }

        private void OnAuthorizationBrowserNavigated(object sender, NavigationEventArgs e)
        {
            this.authorizationBrowser.Navigated -= this.OnAuthorizationBrowserNavigated;
            this.authorizationBrowser.Visibility = Visibility.Visible;
        }


        private void OnAuthorizationBrowserNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            if (getAccessTokenCompleted != null && !getAccessTokenCompleted.Task.IsCompleted)
                getAccessTokenCompleted.SetCanceled();
        }

        /// <summary>
        /// Handles the navigating event of the OAuth web browser control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The event data.</param>
        private void OnAuthorizationBrowserNavigating(object sender, NavigatingEventArgs e)
        {
            Uri uri = e.Uri;

            if (uri != null && uri.AbsoluteUri.StartsWith(RedirectUri))
            {
                Dictionary<string, string> fragments = this.ProcessFragments(uri.Fragment);

                bool success = fragments.TryGetValue("access_token", out this.accessToken);

                e.Cancel = true;
                this.CompleteOAuthFlow(success);
            }
        }

        /// <summary>
        /// Process the URI fragment string.
        /// </summary>
        /// <param name="fragment">The URI fragment.</param>
        /// <returns>The key-value pairs.</returns>
        private Dictionary<string, string> ProcessFragments(string fragment)
        {
            Dictionary<string, string> processedFragments = new Dictionary<string, string>();

            if (fragment[0] == '#')
            {
                fragment = fragment.Substring(1);
            }

            string[] fragmentParams = fragment.Split('&');

            foreach (string fragmentParam in fragmentParams)
            {
                string[] keyValue = fragmentParam.Split('=');

                if (keyValue.Length == 2)
                {
                    processedFragments.Add(keyValue[0], HttpUtility.UrlDecode(keyValue[1]));
                }
            }

            return processedFragments;
        }

        /// <summary>
        /// Build the API service URI.
        /// </summary>
        /// <param name="path">The relative path requested.</param>
        /// <returns>The request URI.</returns>
        private static Uri BuildApiUri(string accessToken, string path, IDictionary<string, string> parameters = null)
        {
            UriBuilder builder = new UriBuilder(ApiServiceUri);
            builder.Path += path;
            builder.Query = (parameters != null && parameters.Count() > 0 ? string.Join("&", parameters.Select(pair => pair.Key + "=" + pair.Value).ToArray()) + "&" : "") + "access_token=" + HttpUtility.UrlEncode(accessToken);
            return builder.Uri;
        }

        /// <summary>
        /// Build the OAuth URI.
        /// </summary>
        /// <param name="scopes">The requested scopes.</param>
        /// <returns>The OAuth URI.</returns>
        private static Uri BuildOAuthUri(string[] scopes)
        {
            List<string> paramList = new List<string>();
            paramList.Add("client_id=" + HttpUtility.UrlEncode(ClientId));
            paramList.Add("scope=" + HttpUtility.UrlEncode(String.Join(" ", scopes)));
            paramList.Add("response_type=" + HttpUtility.UrlEncode("token"));
            paramList.Add("display=" + HttpUtility.UrlEncode("touch"));
            paramList.Add("redirect_uri=" + HttpUtility.UrlEncode(RedirectUri));

            UriBuilder authorizeUri = new UriBuilder(OAuthAuthorizeUri);
            authorizeUri.Query = String.Join("&", paramList.ToArray());
            return authorizeUri.Uri;
        }

        public async Task<IList<WLFolder>> GetFolders()
        {
            var parameters = new Dictionary<string, string> { { "filter", "folders" } };
            var uri = BuildApiUri(accessToken, "me/skydrive/files", parameters);
            var client = WebRequest.Create(uri);
            var response = await client.GetResponseAsync();
            var ser = new DataContractJsonSerializer(typeof(WLFolders));
            var folders = (WLFolders)ser.ReadObject(response.GetResponseStream());
            return folders.data;
        }

        public async Task<WLLink> GetFileInfo(string fileId)
        {
            var uri = BuildApiUri(accessToken, string.Format("{0}/shared_read_link", fileId));
            var client = WebRequest.Create(uri);
            var response = await client.GetResponseAsync();
            var ser = new DataContractJsonSerializer(typeof(WLLink));
            var file = (WLLink)ser.ReadObject(response.GetResponseStream());
            return file;
        }

        public async Task<WLFile> UploadFileToWL(string fileName, string contentType, WLFolder folder, Stream fileStream, IProgress<MultipartPostProgressData> progress = null, CancellationToken cancel = default(CancellationToken))
        {
            var uri = BuildApiUri(accessToken, folder.Id + "/files");
            var client = MultipartPostRequest.Create(uri);
            client.Parts.AddFile("file", fileName, contentType, fileStream);
            var responseStream = await client.PostAsync(progress, cancel);
            //load into memory stream
            var ser2 = new DataContractJsonSerializer(typeof(WLFile));
            var uploadedFile = (WLFile)ser2.ReadObject(responseStream);
            return uploadedFile;
        }

        private byte[] ConvertToByteArray(string s)
        {
            return Encoding.UTF8.GetBytes(s);
        }

        // <summary>
        // Returns a random numbers string
        // </summary>
        private string GetBoundary()
        {
            int boundaryLength = 20;
            Random random = new Random();
            char[] buffer = new char[boundaryLength];
            for (int i = 0; i < boundaryLength; i++)
            {
                buffer[i] = (char)(random.Next(10) + 48);
            }
            return new string(buffer);

        }

        #endregion

    }
    /// <summary>
    /// Represents multiple albums returned in a JSON request
    /// </summary>
    public class WLFolders
    {
        public List<WLFolder> data { get; set; }
    }

    [DataContract]
    public class WLFolder
    {
        [DataMember(Name = "id")]
        public string Id { get; set; }
        [DataMember(Name = "name")]
        public string Name { get; set; }
    }

    [DataContract]
    public class WLFile
    {
        [DataMember(Name = "id")]
        public string Id { get; set; }
        [DataMember(Name = "source")]
        public string Source { get; set; }
    }

    [DataContract]
    public class WLLink
    {
        [DataMember(Name = "link")]
        public string Link { get; set; }
    }
}
